<template>
  <div class="dialog-form"><!-- 包一层div解决element-plus组件类选不中问题，便于选中类后修改样式 -->
    <el-dialog class="dialog-form__dialog" ref="dialog"
      v-model="visibleVariable"
      :before-close="clickCancelDialog"
      v-bind="dialogOptionsVariable.attributes"
      v-on="dialogOptionsVariable.events"
    >
      <template #header
        v-if="dialogOptionsVariable.header.show === 'function'?dialogOptionsVariable.header.show():dialogOptionsVariable.header.show">
        <div class="dialog-form__title">
          <slot name="dialog-title">
            {{ dialogOptionsVariable.header.title }}
          </slot>
        </div>
      </template>

      <!-- 提示 -->
      <el-alert class="dialog-form__alert"
        v-if="dialogOptionsVariable.alert.show === 'function'?dialogOptionsVariable.alert.show():dialogOptionsVariable.alert.show"
        :title="dialogOptionsVariable.alert.title"
        v-bind="dialogOptionsVariable.alert.attributes"
        v-on="dialogOptionsVariable.alert.events"
      />

      <slot name="dialog-body-top"></slot>

      <!-- 表单 -->
      <slot>
        <el-form class="dialog-form__form" ref="form"
          :model="formModel"
          :rules="formOptionsVariable.rules"
          v-bind="formOptionsVariable.attributes"
          v-on="formOptionsVariable.events"
        >
          <div class="dialog-form__form__body" :class="`grid-cols-${formOptionsVariable.columnNumber}`">
            <template v-for="(formItem,index) in formOptionsVariable.formItems" :key="'form-item' + index">
              <el-form-item
                v-if="formItem.show === 'function'?formItem.show(formModel):formItem.show"
                class="dialog-form__form__item"
                :class="`col-span-${formItem.grid}`"
                :prop="formItem.prop"
                :label="formItem.label"
                v-bind="formItem.formAttr"
              >
                <!-- 表单项名称 -->
                <template #label>
                  <div class="dialog-form__form__item__label">
                    <slot :name="'form-item-label-' + formItem.prop" :formItem="formItem">
                      <span>{{ formItem.label }}</span>

                      <el-tooltip v-if="formItem.tip" :content="formItem.tip">
                        <el-icon class="cursor-pointer">
                          <QuestionFilled/>
                        </el-icon>
                      </el-tooltip>
                    </slot>
                  </div>
                </template>

                <!-- 表单项组件 -->
                <div class="dialog-form__form__item__component">
                  <slot :name="'form-item-component-' + formItem.prop" :formItem="formItem">
                    <el-input class="w-full"
                      v-if="formItem.type === ArkDialogFormConstant.operation.input"
                      v-model.trim="formModel[formItem.prop]"
                      v-bind="formItem.attributes"
                      v-on="formItem.events"
                    />

                    <el-select class="w-full"
                      v-else-if="formItem.type === ArkDialogFormConstant.operation.select"
                      v-model="formModel[formItem.prop]"
                      v-bind="formItem.attributes"
                      v-on="formItem.events"
                    >
                      <template v-if="formItem.selectOptions.groups">
                        <el-option-group v-for="(groupItem,groupIndex) in formItem.selectOptions.groups"
                          :key="'form-item-component-select'+groupIndex"
                          :label="groupItem.label"
                        >
                          <el-option v-for="(optionItem,optionIndex) in groupItem.options"
                            :key="'form-item-component-select'+groupIndex+optionIndex"
                            :label="optionItem.label"
                            :value="optionItem.value"
                          />
                        </el-option-group>
                      </template>
                      <template v-else-if="formItem.selectOptions.options">
                        <el-option v-for="(optionItem,index) in formItem.selectOptions.options"
                          :key="'form-item-component-select'+index"
                          :label="optionItem.label"
                          :value="optionItem.value"
                        />
                      </template>
                    </el-select>

                    <el-cascader class="w-full"
                      v-else-if="formItem.type === ArkDialogFormConstant.operation.cascader"
                      v-model="formModel[formItem.prop]"
                      v-bind="formItem.attributes"
                      v-on="formItem.events"
                    />

                    <el-date-picker class="w-full"
                      v-else-if="formItem.type === ArkDialogFormConstant.operation.datePicker"
                      v-model="formModel[formItem.prop]"
                      v-bind="formItem.attributes"
                      v-on="formItem.events"
                    />

                    <el-time-picker class="w-full"
                      v-else-if="formItem.type === ArkDialogFormConstant.operation.timePicker"
                      v-model="formModel[formItem.prop]"
                      v-bind="formItem.attributes"
                      v-on="formItem.events"
                    />

                    <el-time-select class="w-full"
                      v-else-if="formItem.type === ArkDialogFormConstant.operation.timeSelect"
                      v-model="formModel[formItem.prop]"
                      v-bind="formItem.attributes"
                      v-on="formItem.events"
                    />
                  </slot>
                </div>
              </el-form-item>
            </template>
          </div>
        </el-form>
      </slot>

      <slot name="dialog-body-bottom"></slot>

      <template #footer
        v-if="dialogOptionsVariable.footer.show === 'function'?dialogOptionsVariable.footer.show():dialogOptionsVariable.footer.show">
        <slot name="dialog-footer">
          <el-button
            v-if="dialogOptionsVariable.footer.cancel.show === 'function'?dialogOptionsVariable.footer.cancel.show():dialogOptionsVariable.footer.cancel.show"
            v-bind="dialogOptionsVariable.footer.cancel.attributes"
            v-on="dialogOptionsVariable.footer.cancel.events"
            @click="clickCancelDialog()"
          >
            {{ dialogOptionsVariable.footer.cancel.text }}
          </el-button>

          <el-button
            v-if="dialogOptionsVariable.footer.confirm.show === 'function'?dialogOptionsVariable.footer.confirm.show():dialogOptionsVariable.footer.confirm.show"
            v-bind="dialogOptionsVariable.footer.confirm.attributes"
            v-on="dialogOptionsVariable.footer.confirm.events"
            @click="clickConfirmDialog()"
            :loading="confirmLoading"
          >
            {{ dialogOptionsVariable.footer.confirm.text }}
          </el-button>
        </slot>
      </template>
    </el-dialog>
  </div>
</template>

<script>
import {defineComponent} from 'vue';
import _ from 'lodash';
import ArkDialogFormConstant from './ArkDialogFormConstant.js';

export default defineComponent({
  name: 'ArkDialogForm',
  emits: ['update:visible'],
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    /**
     * 对话框配置对象
     *  - beforeClose：关闭对话框前的回调
     *  - attributes：对话框v-bind对象
     *  - events：对话框v-on对象
     *  - header：页眉，不包含×
     *   - show：是否展示。可为boolean类型，可为方法
     *   - title：标题
     *  - alert：对话框主体顶部提示
     *   - show：是否展示。可为boolean类型，可为方法
     *   - title：提示文字
     *   - attributes：提示v-bind对象
     *   - events：提示v-on对象
     *  - footer：页脚按钮
     *   - show：是否展示。可为boolean类型，可为方法
     *   - confirm：确认按钮
     *    - show：是否展示。可为boolean类型，可为方法
     *    - attributes：按钮v-bind对象
     *    - events：按钮v-on对象
     *    - click：按钮click事件，参数formModel
     *    - text：按钮文字
     *   - cancel：取消按钮
     *    - show：是否展示。可为boolean类型，可为方法
     *    - attributes：按钮v-bind对象
     *    - events：按钮v-on对象
     *    - text：按钮文字
     */
    dialogOptions: {
      type: Object,
    },
    /**
     * 表单配置对象
     *  - attributes：表单v-bind对象
     *  - events：表单v-on对象
     *  - rules：表单项验证规则
     *  - validateFail：表单项验证失败，参数为验证失败数据
     *  - columnNumber：表单内部列数
     *  - formItems[]：表单项v-bind对象与其他配置
     *   - show：是否展示。可为boolean类型，可为方法。为方法时参数formModel，返回boolean类型
     *   - defaultShow：表单项是否展示默认值，默认等于show
     *   - grid：该表单项占网格布局中的几列
     *   - prop：与formModel对象的键对应
     *   - label：标签文字
     *   - fromAttr：表单v-bind对象
     *   - tip：标签文字提示内容
     *   - type：表单项组件类型
     *   - defaultValue：表单项组件默认值
     *   - attributes：表单项组件v-bind对象
     *   - selectOptions：表单项组件选择框select的选项。不分组对象内容为{options[{label,value}]}，分组对象内容为{groups[{label,options[{label,value}]}]}
     *   - events：表单项组件v-on对象
     */
    formOptions: {
      type: Object,
    },
  },
  data() {
    const thisVue = this;
    return {
      ArkDialogFormConstant: ArkDialogFormConstant,
      dialogOptionsVariable: {
        beforeClose: () => { },
        attributes: {
          'close-on-click-modal': false,
        },
        events: {},
        header: {
          show: true,
          title: '提示',
        },
        alert: {
          show: true,
          title: '对话框主体顶部提示',
          attributes: {
            type: 'info',
            'show-icon': true,
            closable: false,
          },
          events: {},
        },
        footer: {
          show: true,
          confirm: {
            show: true,
            attributes: {
              type: 'primary',
            },
            events: {},
            click: () => { },
            text: '确定'
          },
          cancel: {
            show: true,
            attributes: {},
            events: {},
            text: '取消'
          },
        }
      },
      formOptionsVariable: {
        attributes: {
          'label-position': 'right',
          'label-width': 'auto',
        },
        events: {},
        rules: {},
        validateFail: () => { },
        columnNumber: 1,
        formItems: [],
      },
      confirmLoading: false,
      formModel: {},
    };
  },
  computed: {
    visibleVariable: {
      get() {
        return this.visible;
      },
      set(value) {
        this.$emit('update:visible', value);
      }
    }
  },
  created() {
    this.initAllOptionsVariable();
  },
  methods: {
    initAllOptionsVariable() {
      // 根据传入dialogOptions初始化dialogOptionsVariable
      const dialogOptions = _.cloneDeep(this.dialogOptions);
      const dialogOptionsVariable = _.cloneDeep(this.dialogOptionsVariable);
      this.dialogOptionsVariable = _.defaultsDeep(dialogOptions, dialogOptionsVariable);

      // 根据传入formOptions初始化formOptionsVariable
      const formOptions = _.cloneDeep(this.formOptions);
      const formOptionsVariable = _.cloneDeep(this.formOptionsVariable);
      this.formOptionsVariable = this.setFormOptionsVariable(formOptions, formOptionsVariable);
    },
    setFormOptionsVariable(options, optionsVariable) {
      optionsVariable = _.defaultsDeep(options, optionsVariable);
      optionsVariable.formItems.forEach((item, index) => {
        if (item.show === undefined) {
          item.show = true;
        }

        const formItemsOptionsVariable = {
          show: true,
          defaultShow: item.show,
          grid: 1,
          prop: index.toString(),
          label: '表单项名称',
          formAttr: {},
          tip: item.tip,
          type: ArkDialogFormConstant.operation.input,
          defaultValue: '',
          attributes: item.type === ArkDialogFormConstant.operation.select ?
            {placeholder: '请选择', clearable: true} : {placeholder: '请输入', clearable: true},
          selectOptions: item.type === ArkDialogFormConstant.operation.select ? {options: []} : null,
          events: {},
        };

        _.defaultsDeep(item, formItemsOptionsVariable);

        this.formModel[item.prop] = item.defaultValue;
      });

      return optionsVariable;
    },
    clickConfirmDialog() {
      let form = this.$refs.form;
      if (!form) {
        this.confirmLoading = true;

        Promise.resolve(this.dialogOptionsVariable.footer.confirm.click(this.formModel)).finally(() => {
          this.confirmLoading = false;
        });
        return;
      }

      form.validate((isValid, invalidFields) => {
        if (!isValid) {
          this.formOptionsVariable.validateFail(invalidFields);
          return;
        }

        this.confirmLoading = true;
        Promise.resolve(this.dialogOptionsVariable.footer.confirm.click(this.formModel)).finally(() => {
          this.confirmLoading = false;
        });
      });
    },
    clickCancelDialog() {
      if (typeof this.dialogOptionsVariable.beforeClose === 'function') {
        Promise.resolve(this.dialogOptionsVariable.beforeClose()).then(() => {
          this.visibleVariable = false;
          this.resetFormModel();
        });
        return;
      }
      this.visibleVariable = false;
      this.resetFormModel();
    },
    resetFormModel() {
      let form = this.$refs.form;
      if (form) {
        form.resetFields();
      }

      this.formOptionsVariable.formItems.forEach((item) => {
        item.show = item.defaultShow;
      });
    },

    // 供外部调用，内部未使用
    changeDialogVisible(visible) {
      this.visibleVariable = visible;

      if (!visible) {
        this.clickCancelDialog();
      }
    },
    getElDialogInstance() {
      return this.$refs.dialog;
    },
    getElFormInstance() {
      return this.$refs.form;
    },
    setFormModel(formModel) {
      this.formModel = _.defaultsDeep(formModel, this.formModel);
    },
    setFormItemShow(prop, show) {
      this.formOptionsVariable.formItems.forEach(item => {
        if (item.prop !== prop) {
          return;
        }

        item.show = show;
      });
    },
  },
});
</script>

<style scoped>
.dialog-form {
  :deep(.el-overlay) {
    .dialog-form__dialog {
      @apply rounded-xl;

      .el-dialog__header {
        .dialog-form__title {
          @apply text-xl text-[#303133] leading-6;
        }
      }

      .el-dialog__body {
        @apply pt-[3%] px-[10%];

        .dialog-form__alert {
          @apply mt-4 mb-5;
        }

        .dialog-form__form {
          .dialog-form__form__body {
            @apply grid;

            .dialog-form__form__item {
              @apply flex items-center gap-x-2;

              .el-form-item__label {
                display: flex;
              }

              .dialog-form__form__item__label {
                @apply shrink-0 flex items-center gap-x-0.5;
              }

              .dialog-form__form__item__component {
                @apply flex-1;

                & > div {
                  @apply w-full;
                }
              }
            }
          }
        }
      }
    }
  }
}
</style>
